import wx
import sys

class HintText(wx.TextCtrl):
	def __init__(self, parent, **kwargs):
		self.choices = kwargs.pop("choices")
		self.choices.sort()
		wx.TextCtrl.__init__(self, parent, **kwargs)

		self.dropwin = wx.PopupWindow(self)

		self.droplist = wx.ListBox(self.dropwin, choices=self.choices, style=wx.LB_SINGLE)
		self.Bind(wx.EVT_TEXT, self.OnTextChanged)
		self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
		self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
		gp = self
		while ( gp != None ) :
			gp.Bind(wx.EVT_MOVE, self.OnMove, gp)
			#gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp )
			gp = gp.GetParent()

		self.droplist.Bind(wx.EVT_LEFT_DOWN, self.OnListClick)
		self.droplist.Bind(wx.EVT_MOTION, self.OnListMouseMove)

	def OnMove(self, evt):
		s = self.GetSize()
		x, y = self.ClientToScreenXY(0, s.y)
		bw, bh = self.droplist.GetWindowBorderSize()
		self.droplist.SetSize((s.x, 
			bh + 5 * self.droplist.GetCharHeight()))
		self.dropwin.SetPosition((x-bw/2, y))

		evt.Skip()

	def OnKillFocus(self, evt):
		self.show_dropdown(False)

	def OnKeyDown(self, evt):
		pass

	def OnTextChanged(self, evt):
		if not self.dropwin.IsShown():
			self.show_dropdown(True)

		text = self.GetValue()
		i = 0
		found = False
		for v in self.choices:
			if v.startswith(text):
				self.droplist.Select(i)
				self.droplist.SetFirstItem(i)
				found = True
				break;
			i+=1

		if not found:
			self.droplist.Select(-1)

		evt.Skip()

	def OnListClick(self, evt):
		pass

	def OnListMouseMove(self, evt):
		old = self.droplist.GetSelection()
		sel = self.droplist.HitTest(evt.GetPosition())
		if sel == -1 or old == sel: return
		self.droplist.Select(sel)

	def show_dropdown(self, show):
		if show:
			s = self.GetSize()
			x, y = self.ClientToScreenXY(0, s.y)
			bw, bh = self.droplist.GetWindowBorderSize()
			self.droplist.SetSize((s.x, 
				bh + 5 * self.droplist.GetCharHeight()))
			self.dropwin.Size = self.droplist.Size
			self.dropwin.SetPosition((x-bw/2, y))
			self.droplist.Raise()

		self.dropwin.Show(show)

	def _fetch_selected(self):
		pass

class AutoCompleteText(HintText):
	def __init__(self, parent, **kwargs):
		HintText.__init__(self, parent, **kwargs)

	def OnListClick(self, evt):
		sel = self.droplist.HitTest(evt.GetPosition())
		if sel == -1: return
		self._fetch_selected()
		self.show_dropdown(False)

	def OnKeyDown(self, evt):
		if not self.dropwin.IsShown():
			evt.Skip()
			return

		keycode = evt.GetKeyCode()
		sel = self.droplist.GetSelection()
		skip = True

		if keycode == wx.WXK_DOWN:
			if sel < self.droplist.GetCount() - 1:
				self.droplist.Select(sel + 1)
			skip = False
		elif keycode == wx.WXK_UP:
			if sel > 0:
				self.droplist.Select(sel - 1)
			skip = False
		elif keycode == wx.WXK_RETURN:
			self._fetch_selected()
			skip = False
		elif keycode == wx.WXK_ESCAPE:
			self.show_dropdown(False)
			skip = False
		evt.Skip(skip)

	def _fetch_selected(self):
		sel = self.droplist.GetSelection()
		if sel == -1:
			return
		self.Value = self.droplist.GetString(sel)
		self.SetInsertionPointEnd()

class TooltipText(HintText):
	def __init__(self, parent, **kwargs):
		HintText.__init__(self, parent, **kwargs)

		self.Bind(wx.EVT_SET_FOCUS, self.OnFocus)

	def OnFocus(self, evt):
		if not self.dropwin.IsShown():
			self.show_dropdown(True)
		evt.Skip()

	def OnListClick(self, evt):
		sel = self.droplist.HitTest(evt.GetPosition())
		if sel == -1: return
		self._fetch_selected()
		self.droplist.Select(sel)

	def OnKeyDown(self, evt):
		skip = True
		keycode = evt.GetKeyCode()

		if not self.dropwin.IsShown():
			if keycode == wx.WXK_ESCAPE:
				self.show_dropdown(True)
				skip = False
			evt.Skip(skip)
			return

		sel = self.droplist.GetSelection()

		if keycode == wx.WXK_DOWN:
			if sel < self.droplist.GetCount() - 1:
				self.droplist.Select(sel + 1)
			skip = False
		elif keycode == wx.WXK_UP:
			if sel > 0:
				self.droplist.Select(sel - 1)
			skip = False
		elif keycode == wx.WXK_ESCAPE:
			self.show_dropdown(False)
			skip = False
		evt.Skip(skip)

	def _fetch_selected(self):
		sel = self.droplist.GetSelection()
		if sel == -1:
			return
		start, end = self.GetSelection()
		orig = self.Value
		ins = self.droplist.GetString(sel)
		self.Value = orig[:start] + ins + orig[end:]
		self.SetInsertionPoint(start + len(ins))

if __name__ == '__main__':
	choices = ["self",
			"droplist",
			"insert",
			"string",
			"popup",
			"dropdown",
			"show",
			"me",
			"the",
			"money",
			]
	app = wx.PySimpleApp(False)
	frame = wx.Frame(None, -1, size=(300, 400))
	gbs = wx.GridBagSizer(5, 5)
	at = TooltipText(frame, choices=choices)
	gbs.Add(at, (1, 1))
	frame.SetSizer(gbs)
	frame.Show()
	app.MainLoop()
